package Audio;

import Engine.Scene.Map;
import java.io.IOException;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;

/**
 * It plays the corresponding music depending of the scene or menu
 * @author Gerardo Galíndez
 */
public class MusicPlayer {

    //---------------------------------------
    //Object references----------------------
    private Player audio;
    private boolean playing;
    private long lastMediaTime;
    private int currentTrack = -1;
    //Menu music constant--------------------
    /**
     * MusicPlayer integer index code representing the audio of the main menu.
     */
    public static final int MENU_MUSIC = 15;
    /**
     * Integer index code representing a normal battle music
     */
    public static final int BATTLE_MUSIC_NORMAL_1 = 0;
    /**
     * Integer index code representing a normal battle music
     */
    public static final int BATTLE_MUSIC_NORMAL_2 = 1;
    /**
     * Integer index code representing a normal battle music
     */
    public static final int BATTLE_MUSIC_NORMAL_3 = 2;
    /**
     * Integer index code representing a boss battle music
     */
    public static final int BATTLE_MUSIC_BOSS_1 = 14;
    /**
     * Integer index code representing a boss battle music
     */
    public static final int BATTLE_MUSIC_BOSS_2 = 15;
    /**
     * Integer index code representing a boss battle music
     */
    public static final int BATTLE_MUSIC_BOSS_3 = 16;
    //---------------------------------------

    //<<--------------------------------------------Object management----------------------------------------------->>

    /**
     * Creates a new MusicPlayer object which creates a Player object containing the MIDI sequence of the selected scene. The audio plays until
     * it is requested to stop.
     * @param mapCode Map integer index constant representing the current scene.
     */
    public MusicPlayer(int mapCode) {
        this.audio = this.getMapMusic(mapCode);
        this.audio.setLoopCount(-1);
        this.playing = true;
        try {
            this.audio.start();
        } catch (MediaException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Deploys the corresponding Player object for a selected scene and sets the variable currentTrack to the integer index constant that
     * represents the current scene.
     * @param menuCode Map integer index code representing the current scene.
     * @return Corresponding Player object.
     */
    public Player getMapMusic(int menuCode) {
        try {
            switch (menuCode) {
                case MENU_MUSIC:
                    currentTrack = MENU_MUSIC;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/MainMenu.mid"), "audio/midi");
                case Map.FISHBOWL:
                    currentTrack = Map.FISHBOWL;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Fishbowl.mid"), "audio/midi");
                case Map.OUTER_FISHBOWL:
                    currentTrack = Map.OUTER_FISHBOWL;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/OuterFishbowl.mid"), "audio/midi");
                case Map.DISTILLERY:
                    currentTrack = Map.DISTILLERY;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Distillery.mid"), "audio/midi");
                case Map.OUTER_DISTILLERY:
                    currentTrack = Map.OUTER_DISTILLERY;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/OuterFishbowl.mid"), "audio/midi");
                case Map.OUTSIDES:
                    currentTrack = Map.OUTSIDES;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Outsides.mid"), "audio/midi");
                case Map.WOLVES_LOWER_LEFT:
                    currentTrack = Map.WOLVES_LOWER_LEFT;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Wolves.mid"), "audio/midi");
                case Map.WOLVES_LOWER_RIGHT:
                    currentTrack = Map.WOLVES_LOWER_RIGHT;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Wolves.mid"), "audio/midi");
                case Map.WOLVES_UPPER_LEFT:
                    currentTrack = Map.WOLVES_UPPER_LEFT;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Wolves.mid"), "audio/midi");
                case Map.WOLVES_UPPER_RIGHT:
                    currentTrack = Map.WOLVES_UPPER_RIGHT;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/Wolves.mid"), "audio/midi");
                case Map.CCI_RECEIVER:
                    currentTrack = Map.CCI_RECEIVER;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/CCI.mid"), "audio/midi");
                case Map.CCI_COMPUTERS:
                    currentTrack = Map.CCI_COMPUTERS;
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/CCI.mid"), "audio/midi");
                default:
                    this.audio = null;
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (MediaException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Select the normal battle music according to a battle code
     * @param battleCode representing the type of music
     * @return the audio for the battle
     */
    public Player getBattleMusic(int battleCode) {
        try {
            switch (battleCode) {
                case BATTLE_MUSIC_NORMAL_1:
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/NormalBattle1.mid"), "audio/midi");
                case BATTLE_MUSIC_NORMAL_2:
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/NormalBattle2.mid"), "audio/midi");
                case BATTLE_MUSIC_NORMAL_3:
                    return Manager.createPlayer(getClass().getResourceAsStream("/Sound/Music/NormalBattle3.mid"), "audio/midi");
                default:
                    System.out.println("Bad battle code.");
                    this.audio = null;
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (MediaException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Calls cancelPlayer() and sets the Player object to null, in order to be garbage collected.
     */
    public void shutDown() {
        this.cancelPlayer();
        this.audio = null;
    }
    //<<------------------------------------------------------------------------------------------------------------>>

    //<<--------------------------------------------Player manipulation--------------------------------------------->>
    /**
     * Stops, deallocates and closes the Player object in use.
     */
    public void cancelPlayer() {
        try {
            this.audio.stop();
            this.audio.deallocate();
            this.audio.close();
        } catch (MediaException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Checks if the current playing track is the same as the one requested. If not, it cancels the current player and reconstructs the object
     * with a new MIDI file.
     * @param mapCode Map integer index code representing the current scene.
     */
    public void changeTrack(int mapCode) {
        //if (mapCode != currentTrack) {
            this.cancelPlayer();
            this.audio = this.getMapMusic(mapCode);
            this.audio.setLoopCount(-1);
            this.playing = true;
            try {
                this.audio.start();
            } catch (MediaException ex) {
                ex.printStackTrace();
            }
        //}
    }

    /**
     * Checks the player state and resumes or stops playback. When resuming, if the Player's media time is past the first second, the Player's
     * media time is set to the previously stored time in lastMediaTime, minus one second. If not, it simply resumes in the previous media time.
     * In both cases, the boolean variable playing changes to true. When stopping, the media time is recorded in the variable lastMediaTime,
     * the Player is stopped and the boolean variable is changed to false.
     */
    public void toggleMusic() {
        System.out.println("In. " + playing);
        if (playing) {
            try {
                lastMediaTime = this.audio.getMediaTime();
                this.audio.stop();
                playing = false;
            } catch (MediaException ex) {
                ex.printStackTrace();
            }
        } else {
            try {
                if (lastMediaTime - 1000 > 0) {
                    this.audio.setMediaTime(lastMediaTime - 1000);
                } else {
                    this.audio.setMediaTime(lastMediaTime);
                }
                this.audio.start();
                playing = true;
            } catch (MediaException ex) {
                ex.printStackTrace();
            }
        }
    }

    public void resume() {
        if(!playing) {
            try {
                if (lastMediaTime - 1000 > 0) {
                    this.audio.setMediaTime(lastMediaTime - 1000);
                } else {
                    this.audio.setMediaTime(lastMediaTime);
                }
                this.audio.start();
                playing = true;
            } catch (MediaException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Runs the music for the battle
     * @param musicCode integer code representing the type of battle
     */
    public void startBattleMusic(int musicCode) {
        System.out.println("Battle music code received: " + musicCode);
        this.cancelPlayer();
        this.audio = getBattleMusic(musicCode);
        this.audio.setLoopCount(-1);
        this.playing = true;
        try {
            this.audio.start();
        } catch (MediaException ex) {
            ex.printStackTrace();
        }
    }

    //<<------------------------------------------------------------------------------------------------------------>>
}